# Install required libraries
packages <- c('leaflet','dplyr','data.table','sp', 'rgeos', 'raster', 
'rgdal','GISTools','magrittr','BSDA', 'PASWR','broom','tidyverse','gtools')

for(p in packages){
  if(!require(p,character.only = T)){
    install.packages(p)
  }
  library(p,character.only = T)
}
Loading required package: leaflet
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
Loading required package: dplyr
Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union

Loading required package: data.table
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
data.table 1.12.8 using 4 threads (see ?getDTthreads).  Latest news: r-datatable.com

Attaching package: ‘data.table’

The following objects are masked from ‘package:dplyr’:

    between, first, last

Loading required package: sp
Loading required package: rgeos
rgeos version: 0.5-2, (SVN revision 621)
 GEOS runtime version: 3.7.2-CAPI-1.11.2 
 Linking to sp version: 1.3-1 
 Polygon checking: TRUE 

Loading required package: raster

Attaching package: ‘raster’

The following object is masked from ‘package:data.table’:

    shift

The following object is masked from ‘package:dplyr’:

    select

Loading required package: rgdal
rgdal: version: 1.4-8, (SVN revision 845)
 Geospatial Data Abstraction Library extensions to R successfully loaded
 Loaded GDAL runtime: GDAL 2.4.2, released 2019/06/28
 Path to GDAL shared files: /Users/jayneteo/Library/R/3.6/library/rgdal/gdal
 GDAL binary built with GEOS: FALSE 
 Loaded PROJ.4 runtime: Rel. 5.2.0, September 15th, 2018, [PJ_VERSION: 520]
 Path to PROJ.4 shared files: /Users/jayneteo/Library/R/3.6/library/rgdal/proj
 Linking to sp version: 1.3-2 
Loading required package: GISTools
Loading required package: maptools
Checking rgeos availability: TRUE
Loading required package: RColorBrewer
Loading required package: MASS

Attaching package: ‘MASS’

The following objects are masked from ‘package:raster’:

    area, select

The following object is masked from ‘package:dplyr’:

    select

Loading required package: magrittr

Attaching package: ‘magrittr’

The following object is masked from ‘package:raster’:

    extract

Loading required package: BSDA
Loading required package: lattice

Attaching package: ‘BSDA’

The following object is masked from ‘package:datasets’:

    Orange

Loading required package: PASWR
Loading required package: e1071

Attaching package: ‘e1071’

The following object is masked from ‘package:raster’:

    interpolate


Attaching package: ‘PASWR’

The following objects are masked from ‘package:BSDA’:

    Chips, CIsim, Combinations, Depend, EDA, Engineer, Grades, Kinder, normarea, nsize, ntester, Phone, Rat, Salinity, SIGN.test, Soccer, SRS, tsum.test, Wool, z.test, zsum.test

Loading required package: broom
Loading required package: tidyverse
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ ggplot2 3.3.0     ✓ purrr   0.3.3
✓ tibble  2.1.3     ✓ stringr 1.4.0
✓ tidyr   1.0.2     ✓ forcats 0.4.0
✓ readr   1.3.1     
── Conflicts ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x data.table::between() masks dplyr::between()
x tidyr::extract()      masks magrittr::extract(), raster::extract()
x dplyr::filter()       masks stats::filter()
x data.table::first()   masks dplyr::first()
x dplyr::lag()          masks stats::lag()
x data.table::last()    masks dplyr::last()
x MASS::select()        masks raster::select(), dplyr::select()
x purrr::set_names()    masks magrittr::set_names()
x purrr::transpose()    masks data.table::transpose()
Loading required package: gtools

Attaching package: ‘gtools’

The following object is masked from ‘package:e1071’:

    permutations
'%!in%' <- function(x,y)!('%in%'(x,y))
# Install required libraries
data <- read.csv("data/realis2018.csv")
head(data)

Problem Description

One day, your mum tells you that we just won the first prize of TOTO which worth 2,500,000 SGD. After you discuss with your family, you decide to buy a flat and plan for the investment. During the period of time, you contact the company “Property Master@” to discuss more on the historical transaction of Singapore property. The sample data givenis “realis2018.csv”. For problems stated below, we use α= 5%.

# clean data

# filter no. of units = 1 cos some transactions are the whole damn building
data %<>% filter(No..of.Units==1)

# recode YISHUN and Yishun
data$Planning.Area <- recode(data$Planning.Area,"YISHUN"="Yishun")

Problem 1

You look around few different planning areas (Column R). When you ask the agent what is the mean Unit price(psm) for Newton flats (Column G), she claims that the mean is higher than 26500. Do you agree with your agent’s suggestion? Explain and justify your answer.

Assumptions:

  • Assume “Flat” means either Condominium, Executive Condominium or Apartment
  • Data is a sample of housing prices in 2018

Code

Newton <-  data %>%  filter(Planning.Area=="Newton",Property.Type %in% c("Condominium", "Apartment", "Executive Condominium"), No..of.Units == "1")
head(Newton)
z.test(Newton$Unit.Price....psm, alternative = "greater", mu= 26500,sigma.x=sd(Newton$Unit.Price....psm))

    One-sample z-Test

data:  Newton$Unit.Price....psm
z = 1.881, p-value = 0.02998
alternative hypothesis: true mean is greater than 26500
95 percent confidence interval:
 26598.75      Inf
sample estimates:
mean of x 
 27286.51 

Detailed Analysis

At 95% Confidence level, we have sufficient evidence to say that the mean price per square meter(psm) of flats in the Newton Area is more than $26500.

Problem 2

Your friend told you that Newton planning area may not be the best area to choose. He suggested you to consider other planning areas. This is a very difficult decision since you need to conduct a more comprehensive analysis and you also need to justify whether you still choose Newton or another planning area.

Assumptions:

  • Assume no preference for planning area
  • Assume purchasing in Q1 2020
  • The budget is 2.5 million
  • Assume “Flat” means either Condominium, Executive Condominium or Apartment
  • Key factors to consider: Accesibility, Estate Maturity
  • Metric for evaluation is PSM

Code

Distribution of Properties <= 2.5 Million across Planning Areas

realis <- fread('data/realis2018.csv')
realis$pa <- toupper(realis$`Planning Area`)
centroids <- readOGR("data/MP14_PLNG_AREA_WEB_PL.shp")
OGR data source with driver: ESRI Shapefile 
Source: "/Users/jayneteo/Desktop/ASSR - Project/TakeHome/data/MP14_PLNG_AREA_WEB_PL.shp", layer: "MP14_PLNG_AREA_WEB_PL"
with 55 features
It has 12 fields
dgp <-  spTransform(centroids, CRS("+proj=longlat +ellps=GRS80"))
one_unit <- subset(realis, realis$`No. of Units` == 1 & realis$`Transacted Price ($)` <= 2500000)
pa_units <- aggregate(realis$`No. of Units`,
                      by = list(realis$pa),
                      FUN = sum)
colnames(pa_units) = c('PA', 'Units')
m <- merge(dgp,pa_units, by.x ='PLN_AREA_N', by.y = 'PA')

pal <-
  colorBin(palette = brewer.pal(10,"YlGnBu"),
           domain = c(0,2000),
           na.color = "#00000000",
           bins=c(0,5,10,50,100,200,400,600,800,1000,1200,1400,1600,1800,2000))
n too large, allowed maximum for palette YlGnBu is 9
Returning the palette you asked for with that many colors
# create the base map, default will be openstreetmap if not selected 
# added centroids point as well
leaflet(dgp) %>% addTiles() %>% 
                 addPolygons(fillColor = ~pal(m$Units),
                             weight = 2,
                             opacity = 1,
                             color = "grey",
                             dashArray = "1",
                             fillOpacity = 0.8) %>% 
                 addLegend("topright", pal, values=(0:2000), 
                           title = "Transacted", 
                           labFormat = labelFormat(suffix = " Units", between = '-')) 
pa_units[order(-pa_units$Units),]

Available Flats by Planning Area

Total Stock

stock_data <- fread("data/stock2019Q4.csv")
stock_data$PA <- toupper(stock_data$PA)
stock <- merge(dgp,stock_data, by.x ='PLN_AREA_N', by.y = 'PA')

pal <-
  colorBin(palette = brewer.pal(10,"YlGnBu"),
           domain = c(0,2000),
           na.color = "#00000000",
           bins=c(0,500,1000,3000,5000,8000,10000,15000,20000,30000))
n too large, allowed maximum for palette YlGnBu is 9
Returning the palette you asked for with that many colors
# create the base map, default will be openstreetmap if not selected 
# added centroids point as well
leaflet(dgp) %>% addTiles() %>% 
                 addPolygons(fillColor = ~pal(stock$Total),
                             weight = 2,
                             opacity = 1,
                             color = "grey",
                             dashArray = "1",
                             fillOpacity = 0.8) %>% 
                 addLegend("topright", pal, values=(0:2000), 
                           title = "Total Stock", 
                           labFormat = labelFormat(suffix = " Units", between = '-')) 
stock_data[order(-stock_data$Total),]

ANOVA and Tukey on PSM per Planning Area for each Flat Type


PropType <- unique(data$Property.Type)
for (k in 1:length(unique(data$Property.Type))){
  data_property <-  data %>%  filter(Property.Type==unique(data$Property.Type)[k])
    res.aov <- aov(Unit.Price....psm.~Planning.Area,data=data_property)
    summary(res.aov)
    results <-  tidy(TukeyHSD(res.aov,ordered=TRUE))
    results_sorted <-  results %>% separate(comparison, c("Bigger", "Smaller"),sep = "-")
    
  
    rankings1 <-  results_sorted %>%  group_by(Bigger) %>%  summarise(Count=n()) %>%  arrange(desc(Count))
    rankings2 <- results_sorted %>% filter(Smaller %!in% rankings1$Bigger) %>%  group_by(Smaller) %>% summarise(Count=n())%>%  arrange(Count)
    names(rankings2)[1] <- "Bigger"
    rankings <- rbind(rankings1,rankings2)

    rankings$Rank <-  seq.int(nrow(rankings)) 
    rankings %<>% dplyr::select(-Count)
    results_sorted <- left_join(results_sorted, rankings) %>%  arrange(Rank)

 
    rankings$TukeyRank <- NA
    rankings$TukeyRank[1] <- 1
    for( i in 2:nrow(rankings)){
      if(results_sorted$adj.p.value[results_sorted$Bigger==rankings$Bigger[i-1]&results_sorted$Smaller == rankings$Bigger[i]]<=0.05){
        rankings$TukeyRank[i] <- rankings$TukeyRank[i-1]+1
      } else if(sum((results_sorted$Smaller[results_sorted$Bigger==rankings$Bigger[i-1]&results_sorted$adj.p.value<=0.05] %in% results_sorted$Smaller[results_sorted$Bigger==rankings$Bigger[i]&results_sorted$adj.p.value>0.05])>0)){
        rankings$TukeyRank[i] <- rankings$TukeyRank[i-1]+1
       } else {
         rankings$TukeyRank[i] <- rankings$TukeyRank[i-1]
       }
     }
    
    Tukey_ranked <-  rankings %>%  dplyr::select(Bigger,TukeyRank)
    names(Tukey_ranked)[1] <-  "Planning.Area"
    Planning_Mean <-  data_property %>% group_by(Planning.Area) %>%  summarise(mean= mean(Unit.Price....psm.), sd= sd(Unit.Price....psm.))
    Tukey_ranked <- left_join(Tukey_ranked, Planning_Mean)
    
    assign(paste("aov_", PropType[k], sep = ""), tidy(res.aov))
    assign(paste("Tukey_", PropType[k], sep = ""), as.data.frame(Tukey_ranked))
}
Joining, by = "Bigger"
Joining, by = "Planning.Area"
Column `Planning.Area` joining character vector and factor, coercing into character vectorJoining, by = "Bigger"
Joining, by = "Planning.Area"
Column `Planning.Area` joining character vector and factor, coercing into character vectorJoining, by = "Bigger"
Joining, by = "Planning.Area"
Column `Planning.Area` joining character vector and factor, coercing into character vectorJoining, by = "Bigger"
Joining, by = "Planning.Area"
Column `Planning.Area` joining character vector and factor, coercing into character vectorJoining, by = "Bigger"
Joining, by = "Planning.Area"
Column `Planning.Area` joining character vector and factor, coercing into character vectorJoining, by = "Bigger"
Joining, by = "Planning.Area"
Column `Planning.Area` joining character vector and factor, coercing into character vector
head(Tukey_Apartment)
head(Tukey_Condominium)
head(`Tukey_Executive Condominium`)
head(`Tukey_Detached House`)
head(`Tukey_Semi-Detached House`)
head(`Tukey_Terrace House`)

summary(res.aov)
                Df    Sum Sq   Mean Sq F value Pr(>F)    
Planning.Area   14 2.071e+09 147906034   216.6 <2e-16 ***
Residuals     1731 1.182e+09    682842                   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
#Detailed analysis 

#We performed the ANOVA test to determine if all the mean PSM per planning areas were similar.
#At 95% Confidence level, as P value is less than 0.05, we have sufficient evidence to say to reject H0 and that the mean price per square meter(psm) of flats are significantly different
#To compare the group means, a post hoc test -  TUKEY test - was performed per property type 
#Given the extensive results output, the TUKEY results were sorted for clarity. 
#For each property type, the differences were sorted, filtered for results with adjusted p-value <=0.05 and then ranked accordingly 
#At 95% confidence intervals:
#   For property type - apartments - the Orchard area compared with the other planning areas, has the most significantly different differences between means. River Valley, Newton and Downtown core are ranked 2nd.
#   For property type - Condominium - the Orchard and River Vallye areas compared with the other planning areas, have the most significantly different differences between means. The Newton area comes in second
#   For property type - EC - the Bishan area compared with the other planning areas, has the most significantly different differences between means.Sengkang, Punggol areas come in second 
#   For property type - Detached House - the Newton area compared with the other planning areas, has the most significantly different differences between means. Tanglin and Novena / Southern islands (Sentosa) are ranked second and third respectively
#   For property type - Semi-Detached House - the River Valley area compared with the other planning areas, has the most significantly different differences between means. The Tanglin, Marine Parade areas are ranked second
#   For property type - Terrance House - the Newton area compared with the other planning areas, has the most significantly different differences between means. Rochor, River Valley and Novea are joint second. 

ANOVA and Tukey on Transacted Price per Planning Area for each Flat Type

# output the damn tukey table and sort
# jayne do anova



for (k in 1:length(unique(data$Property.Type))){
  data_property <-  data %>%  filter(Property.Type==unique(data$Property.Type)[k])
    res.aov <- aov(Transacted.Price....~Planning.Area,data=data_property)
    summary(res.aov)
    results <-  tidy(TukeyHSD(res.aov,ordered=TRUE))
    results_sorted <-  results %>% separate(comparison, c("Bigger", "Smaller"),sep = "-")
    
  
    rankings1 <-  results_sorted %>%  group_by(Bigger) %>%  summarise(Count=n()) %>%  arrange(desc(Count))
    rankings2 <- results_sorted %>% filter(Smaller %!in% rankings1$Bigger) %>%  group_by(Smaller) %>% summarise(Count=n())%>%  arrange(Count)
    names(rankings2)[1] <- "Bigger"
    rankings <- rbind(rankings1,rankings2)

    rankings$Rank <-  seq.int(nrow(rankings)) 
    rankings %<>% dplyr::select(-Count)
    results_sorted <- left_join(results_sorted, rankings) %>%  arrange(Rank)

 
    rankings$TukeyRank <- NA
    rankings$TukeyRank[1] <- 1
    for( i in 2:nrow(rankings)){
      if(results_sorted$adj.p.value[results_sorted$Bigger==rankings$Bigger[i-1]&results_sorted$Smaller == rankings$Bigger[i]]<=0.05){
        rankings$TukeyRank[i] <- rankings$TukeyRank[i-1]+1
      } else if(sum((results_sorted$Smaller[results_sorted$Bigger==rankings$Bigger[i-1]&results_sorted$adj.p.value<=0.05] %in% results_sorted$Smaller[results_sorted$Bigger==rankings$Bigger[i]&results_sorted$adj.p.value>0.05])>0)){
        rankings$TukeyRank[i] <- rankings$TukeyRank[i-1]+1
       } else {
         rankings$TukeyRank[i] <- rankings$TukeyRank[i-1]
       }
     }
    
    Tukey_ranked <-  rankings %>%  dplyr::select(Bigger,TukeyRank)
    names(Tukey_ranked)[1] <-  "Planning.Area"
    Planning_Mean <-  data_property %>% group_by(Planning.Area) %>%  summarise(mean= mean(Transacted.Price....), sd= sd(Transacted.Price....))
    Tukey_ranked <- left_join(Tukey_ranked, Planning_Mean)
    
    assign(paste("aov_", PropType[k], sep = ""), tidy(res.aov))
    assign(paste("Tukey_", PropType[k], sep = ""), as.data.frame(Tukey_ranked))
}
Joining, by = "Bigger"
Joining, by = "Planning.Area"
Column `Planning.Area` joining character vector and factor, coercing into character vectorJoining, by = "Bigger"
Joining, by = "Planning.Area"
Column `Planning.Area` joining character vector and factor, coercing into character vectorJoining, by = "Bigger"
Joining, by = "Planning.Area"
Column `Planning.Area` joining character vector and factor, coercing into character vectorJoining, by = "Bigger"
Joining, by = "Planning.Area"
Column `Planning.Area` joining character vector and factor, coercing into character vectorJoining, by = "Bigger"
Joining, by = "Planning.Area"
Column `Planning.Area` joining character vector and factor, coercing into character vectorJoining, by = "Bigger"
Joining, by = "Planning.Area"
Column `Planning.Area` joining character vector and factor, coercing into character vector
head(Tukey_Apartment)
head(Tukey_Condominium)
head(`Tukey_Executive Condominium`)
head(`Tukey_Detached House`)
head(`Tukey_Semi-Detached House`)
head(`Tukey_Terrace House`)
summary(res.aov)
                Df    Sum Sq   Mean Sq F value Pr(>F)    
Planning.Area   14 1.689e+13 1.206e+12   65.86 <2e-16 ***
Residuals     1731 3.170e+13 1.831e+10                   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
#Detailed analysis 

#We performed the ANOVA test to determine if the mean transacted price per planning areas were similar.
#At 95% Confidence level, as P value is less than 0.05, we have sufficient evidence to say to reject H0 and that the mean transacted price of flats are significantly different
#To compare the group means, a post hoc test -  TUKEY test - was performed for each property type 
#Given the extensive results output, the TUKEY results were sorted for clarity. 
#For each property type, the differences were sorted, filtered for results with adjusted p-value <=0.05 and then ranked accordingly 
#At 95% confidence intervals:
#   For property type - apartments - the Orchard area compared with the other planning areas, has the most significantly different differences between means. Newton and Downtown core are ranked 2nd.This is 
#   For property type - Condominium - the Newton and Tanglin areas compared with the other planning areas, have the most significantly different differences between means. Orchard and River Valley areas come in second
#   For property type - EC - the Bishan area compared with the other planning areas, has the most significantly different differences between means. Ang Mo Kio and Bukit Batok areas are ranked second and third respectively 
#   For property type - Detached House - the Newton area compared with the other planning areas, has the most significantly different differences between means. Tanglin and Southern islands (Sentosa) / Bukit Timah / Novena / Marine Parade are ranked second and third respectively
#   For property type - Semi-Detached House - the Tanglin area compared with the other planning areas, has the most significantly different differences between means. The River Valley area are ranked second
#   For property type - Terrance House - the Newton area compared with the other planning areas, has the most significantly different differences between means. Tanglin and Novena areas are joint second. 

MRT Stations(Existing and Planned) by Planning Area

mrt <- read.csv("data/Planning_area_mrt_stations.csv")
mrt$Planning.Area <- toupper(mrt$ï..Planning.Area)
Error in `$<-.data.frame`(`*tmp*`, Planning.Area, value = character(0)) : 
  replacement has 0 rows, data has 39

Detailed Analysis

LS0tCnRpdGxlOiAiVGFrZS1Ib21lIEdyb3VwIEFzc2lnbm1lbnQiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyfQojIEluc3RhbGwgcmVxdWlyZWQgbGlicmFyaWVzCnBhY2thZ2VzIDwtIGMoJ2xlYWZsZXQnLCdkcGx5cicsJ2RhdGEudGFibGUnLCdzcCcsICdyZ2VvcycsICdyYXN0ZXInLCAKJ3JnZGFsJywnR0lTVG9vbHMnLCdtYWdyaXR0cicsJ0JTREEnLCAnUEFTV1InLCdicm9vbScsJ3RpZHl2ZXJzZScsJ2d0b29scycpCgpmb3IocCBpbiBwYWNrYWdlcyl7CiAgaWYoIXJlcXVpcmUocCxjaGFyYWN0ZXIub25seSA9IFQpKXsKICAgIGluc3RhbGwucGFja2FnZXMocCkKICB9CiAgbGlicmFyeShwLGNoYXJhY3Rlci5vbmx5ID0gVCkKfQoKJyUhaW4lJyA8LSBmdW5jdGlvbih4LHkpISgnJWluJScoeCx5KSkKYGBgCgpgYGB7cn0KIyBJbnN0YWxsIHJlcXVpcmVkIGxpYnJhcmllcwpkYXRhIDwtIHJlYWQuY3N2KCJkYXRhL3JlYWxpczIwMTguY3N2IikKaGVhZChkYXRhKQpgYGAKCiMjIFByb2JsZW0gRGVzY3JpcHRpb24KT25lIGRheSwgeW91ciBtdW0gdGVsbHMgeW91IHRoYXQgd2UganVzdCB3b24gdGhlIGZpcnN0IHByaXplIG9mIFRPVE8gd2hpY2ggd29ydGggMiw1MDAsMDAwIFNHRC4gQWZ0ZXIgeW91IGRpc2N1c3Mgd2l0aCB5b3VyIGZhbWlseSwgeW91IGRlY2lkZSB0byBidXkgYSBmbGF0IGFuZCBwbGFuIGZvciB0aGUgaW52ZXN0bWVudC4gRHVyaW5nIHRoZSBwZXJpb2Qgb2YgdGltZSwgeW91IGNvbnRhY3QgdGhlIGNvbXBhbnkgIlByb3BlcnR5IE1hc3RlckAiIHRvIGRpc2N1c3MgbW9yZSBvbiB0aGUgaGlzdG9yaWNhbCB0cmFuc2FjdGlvbiBvZiBTaW5nYXBvcmUgcHJvcGVydHkuIFRoZSBzYW1wbGUgZGF0YSBnaXZlbmlzIOKAnHJlYWxpczIwMTguY3N24oCdLiBGb3IgcHJvYmxlbXMgc3RhdGVkIGJlbG93LCB3ZSB1c2UgzrE9IDUlLgoKYGBge3J9CiMgY2xlYW4gZGF0YQoKIyBmaWx0ZXIgbm8uIG9mIHVuaXRzID0gMSBjb3Mgc29tZSB0cmFuc2FjdGlvbnMgYXJlIHRoZSB3aG9sZSBkYW1uIGJ1aWxkaW5nCmRhdGEgJTw+JSBmaWx0ZXIoTm8uLm9mLlVuaXRzPT0xKQoKIyByZWNvZGUgWUlTSFVOIGFuZCBZaXNodW4KZGF0YSRQbGFubmluZy5BcmVhIDwtIHJlY29kZShkYXRhJFBsYW5uaW5nLkFyZWEsIllJU0hVTiI9Illpc2h1biIpCmBgYAoKIyBQcm9ibGVtIDEKWW91IGxvb2sgYXJvdW5kIGZldyBkaWZmZXJlbnQgcGxhbm5pbmcgYXJlYXMgKENvbHVtbiBSKS4gV2hlbiB5b3UgYXNrIHRoZSBhZ2VudCB3aGF0IGlzIHRoZSBtZWFuIFVuaXQgcHJpY2UocHNtKSBmb3IgTmV3dG9uIGZsYXRzIChDb2x1bW4gRyksIHNoZSBjbGFpbXMgdGhhdCB0aGUgbWVhbiBpcyBoaWdoZXIgdGhhbiAyNjUwMC4gRG8geW91IGFncmVlIHdpdGggeW91ciBhZ2VudOKAmXMgc3VnZ2VzdGlvbj8gRXhwbGFpbiBhbmQganVzdGlmeSB5b3VyIGFuc3dlci4KCiMjIEFzc3VtcHRpb25zOgoqIEFzc3VtZSAiRmxhdCIgbWVhbnMgZWl0aGVyIENvbmRvbWluaXVtLCBFeGVjdXRpdmUgQ29uZG9taW5pdW0gb3IgQXBhcnRtZW50CiogRGF0YSBpcyBhIHNhbXBsZSBvZiBob3VzaW5nIHByaWNlcyBpbiAyMDE4CgojIyBDb2RlCmBgYHtyfQpOZXd0b24gPC0gIGRhdGEgJT4lICBmaWx0ZXIoUGxhbm5pbmcuQXJlYT09Ik5ld3RvbiIsUHJvcGVydHkuVHlwZSAlaW4lIGMoIkNvbmRvbWluaXVtIiwgIkFwYXJ0bWVudCIsICJFeGVjdXRpdmUgQ29uZG9taW5pdW0iKSwgTm8uLm9mLlVuaXRzID09ICIxIikKaGVhZChOZXd0b24pCmBgYAoKYGBge3J9CnoudGVzdChOZXd0b24kVW5pdC5QcmljZS4uLi5wc20sIGFsdGVybmF0aXZlID0gImdyZWF0ZXIiLCBtdT0gMjY1MDAsc2lnbWEueD1zZChOZXd0b24kVW5pdC5QcmljZS4uLi5wc20pKQpgYGAKCiMjIERldGFpbGVkIEFuYWx5c2lzCkF0IDk1JSBDb25maWRlbmNlIGxldmVsLCB3ZSBoYXZlIHN1ZmZpY2llbnQgZXZpZGVuY2UgdG8gc2F5IHRoYXQgdGhlIG1lYW4gcHJpY2UgcGVyIHNxdWFyZSBtZXRlcihwc20pIG9mIGZsYXRzIGluIHRoZSBOZXd0b24gQXJlYSBpcyBtb3JlIHRoYW4gJDI2NTAwLgoKCiMgUHJvYmxlbSAyCllvdXIgZnJpZW5kIHRvbGQgeW91IHRoYXQgTmV3dG9uIHBsYW5uaW5nIGFyZWEgbWF5IG5vdCBiZSB0aGUgYmVzdCBhcmVhIHRvIGNob29zZS4gSGUgc3VnZ2VzdGVkIHlvdSB0byBjb25zaWRlciBvdGhlciBwbGFubmluZyBhcmVhcy4gVGhpcyBpcyBhIHZlcnkgZGlmZmljdWx0IGRlY2lzaW9uIHNpbmNlIHlvdSBuZWVkIHRvIGNvbmR1Y3QgYSBtb3JlIGNvbXByZWhlbnNpdmUgYW5hbHlzaXMgYW5kIHlvdSBhbHNvIG5lZWQgdG8ganVzdGlmeSB3aGV0aGVyIHlvdSBzdGlsbCBjaG9vc2UgTmV3dG9uIG9yIGFub3RoZXIgcGxhbm5pbmcgYXJlYS4KCiMjIEFzc3VtcHRpb25zOgoqIEFzc3VtZSBubyBwcmVmZXJlbmNlIGZvciBwbGFubmluZyBhcmVhCiogQXNzdW1lIHB1cmNoYXNpbmcgaW4gUTEgMjAyMAoqIFRoZSBidWRnZXQgaXMgMi41IG1pbGxpb24KKiBBc3N1bWUgIkZsYXQiIG1lYW5zIGVpdGhlciBDb25kb21pbml1bSwgRXhlY3V0aXZlIENvbmRvbWluaXVtIG9yIEFwYXJ0bWVudAoqIEtleSBmYWN0b3JzIHRvIGNvbnNpZGVyOiBBY2Nlc2liaWxpdHksIEVzdGF0ZSBNYXR1cml0eQoqIE1ldHJpYyBmb3IgZXZhbHVhdGlvbiBpcyBQU00KCiMjIENvZGUKIyMjIERpc3RyaWJ1dGlvbiBvZiBQcm9wZXJ0aWVzIDw9IDIuNSBNaWxsaW9uIGFjcm9zcyBQbGFubmluZyBBcmVhcwpgYGB7cn0KcmVhbGlzIDwtIGZyZWFkKCdkYXRhL3JlYWxpczIwMTguY3N2JykKcmVhbGlzJHBhIDwtIHRvdXBwZXIocmVhbGlzJGBQbGFubmluZyBBcmVhYCkKY2VudHJvaWRzIDwtIHJlYWRPR1IoImRhdGEvTVAxNF9QTE5HX0FSRUFfV0VCX1BMLnNocCIpCmRncCA8LSAgc3BUcmFuc2Zvcm0oY2VudHJvaWRzLCBDUlMoIitwcm9qPWxvbmdsYXQgK2VsbHBzPUdSUzgwIikpCm9uZV91bml0IDwtIHN1YnNldChyZWFsaXMsIHJlYWxpcyRgTm8uIG9mIFVuaXRzYCA9PSAxICYgcmVhbGlzJGBUcmFuc2FjdGVkIFByaWNlICgkKWAgPD0gMjUwMDAwMCkKcGFfdW5pdHMgPC0gYWdncmVnYXRlKHJlYWxpcyRgTm8uIG9mIFVuaXRzYCwKICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gbGlzdChyZWFsaXMkcGEpLAogICAgICAgICAgICAgICAgICAgICAgRlVOID0gc3VtKQpjb2xuYW1lcyhwYV91bml0cykgPSBjKCdQQScsICdVbml0cycpCm0gPC0gbWVyZ2UoZGdwLHBhX3VuaXRzLCBieS54ID0nUExOX0FSRUFfTicsIGJ5LnkgPSAnUEEnKQoKcGFsIDwtCiAgY29sb3JCaW4ocGFsZXR0ZSA9IGJyZXdlci5wYWwoMTAsIllsR25CdSIpLAogICAgICAgICAgIGRvbWFpbiA9IGMoMCwyMDAwKSwKICAgICAgICAgICBuYS5jb2xvciA9ICIjMDAwMDAwMDAiLAogICAgICAgICAgIGJpbnM9YygwLDUsMTAsNTAsMTAwLDIwMCw0MDAsNjAwLDgwMCwxMDAwLDEyMDAsMTQwMCwxNjAwLDE4MDAsMjAwMCkpCiMgY3JlYXRlIHRoZSBiYXNlIG1hcCwgZGVmYXVsdCB3aWxsIGJlIG9wZW5zdHJlZXRtYXAgaWYgbm90IHNlbGVjdGVkIAojIGFkZGVkIGNlbnRyb2lkcyBwb2ludCBhcyB3ZWxsCmxlYWZsZXQoZGdwKSAlPiUgYWRkVGlsZXMoKSAlPiUgCiAgICAgICAgICAgICAgICAgYWRkUG9seWdvbnMoZmlsbENvbG9yID0gfnBhbChtJFVuaXRzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHQgPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImdyZXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhc2hBcnJheSA9ICIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuOCkgJT4lIAogICAgICAgICAgICAgICAgIGFkZExlZ2VuZCgidG9wcmlnaHQiLCBwYWwsIHZhbHVlcz0oMDoyMDAwKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIlRyYW5zYWN0ZWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiRm9ybWF0ID0gbGFiZWxGb3JtYXQoc3VmZml4ID0gIiBVbml0cyIsIGJldHdlZW4gPSAnLScpKSAKYGBgCgpgYGB7cn0KcGFfdW5pdHNbb3JkZXIoLXBhX3VuaXRzJFVuaXRzKSxdCmBgYAoKIyMjIEF2YWlsYWJsZSBGbGF0cyBieSBQbGFubmluZyBBcmVhCiMjIyMgVG90YWwgU3RvY2sKYGBge3J9CnN0b2NrX2RhdGEgPC0gZnJlYWQoImRhdGEvc3RvY2syMDE5UTQuY3N2IikKc3RvY2tfZGF0YSRQQSA8LSB0b3VwcGVyKHN0b2NrX2RhdGEkUEEpCnN0b2NrIDwtIG1lcmdlKGRncCxzdG9ja19kYXRhLCBieS54ID0nUExOX0FSRUFfTicsIGJ5LnkgPSAnUEEnKQoKcGFsIDwtCiAgY29sb3JCaW4ocGFsZXR0ZSA9IGJyZXdlci5wYWwoMTAsIllsR25CdSIpLAogICAgICAgICAgIGRvbWFpbiA9IGMoMCwyMDAwKSwKICAgICAgICAgICBuYS5jb2xvciA9ICIjMDAwMDAwMDAiLAogICAgICAgICAgIGJpbnM9YygwLDUwMCwxMDAwLDMwMDAsNTAwMCw4MDAwLDEwMDAwLDE1MDAwLDIwMDAwLDMwMDAwKSkKIyBjcmVhdGUgdGhlIGJhc2UgbWFwLCBkZWZhdWx0IHdpbGwgYmUgb3BlbnN0cmVldG1hcCBpZiBub3Qgc2VsZWN0ZWQgCiMgYWRkZWQgY2VudHJvaWRzIHBvaW50IGFzIHdlbGwKbGVhZmxldChkZ3ApICU+JSBhZGRUaWxlcygpICU+JSAKICAgICAgICAgICAgICAgICBhZGRQb2x5Z29ucyhmaWxsQ29sb3IgPSB+cGFsKHN0b2NrJFRvdGFsKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHQgPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImdyZXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhc2hBcnJheSA9ICIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuOCkgJT4lIAogICAgICAgICAgICAgICAgIGFkZExlZ2VuZCgidG9wcmlnaHQiLCBwYWwsIHZhbHVlcz0oMDoyMDAwKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIlRvdGFsIFN0b2NrIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYkZvcm1hdCA9IGxhYmVsRm9ybWF0KHN1ZmZpeCA9ICIgVW5pdHMiLCBiZXR3ZWVuID0gJy0nKSkgCmBgYAoKYGBge3J9CnN0b2NrX2RhdGFbb3JkZXIoLXN0b2NrX2RhdGEkVG90YWwpLF0KYGBgCgojIyMgQU5PVkEgYW5kIFR1a2V5IG9uIFBTTSBwZXIgUGxhbm5pbmcgQXJlYSBmb3IgZWFjaCBGbGF0IFR5cGUKYGBge3J9CgpQcm9wVHlwZSA8LSB1bmlxdWUoZGF0YSRQcm9wZXJ0eS5UeXBlKQpmb3IgKGsgaW4gMTpsZW5ndGgodW5pcXVlKGRhdGEkUHJvcGVydHkuVHlwZSkpKXsKICBkYXRhX3Byb3BlcnR5IDwtICBkYXRhICU+JSAgZmlsdGVyKFByb3BlcnR5LlR5cGU9PXVuaXF1ZShkYXRhJFByb3BlcnR5LlR5cGUpW2tdKQogICAgcmVzLmFvdiA8LSBhb3YoVW5pdC5QcmljZS4uLi5wc20uflBsYW5uaW5nLkFyZWEsZGF0YT1kYXRhX3Byb3BlcnR5KQogICAgc3VtbWFyeShyZXMuYW92KQogICAgcmVzdWx0cyA8LSAgdGlkeShUdWtleUhTRChyZXMuYW92LG9yZGVyZWQ9VFJVRSkpCiAgICByZXN1bHRzX3NvcnRlZCA8LSAgcmVzdWx0cyAlPiUgc2VwYXJhdGUoY29tcGFyaXNvbiwgYygiQmlnZ2VyIiwgIlNtYWxsZXIiKSxzZXAgPSAiLSIpCiAgICAKICAKICAgIHJhbmtpbmdzMSA8LSAgcmVzdWx0c19zb3J0ZWQgJT4lICBncm91cF9ieShCaWdnZXIpICU+JSAgc3VtbWFyaXNlKENvdW50PW4oKSkgJT4lICBhcnJhbmdlKGRlc2MoQ291bnQpKQogICAgcmFua2luZ3MyIDwtIHJlc3VsdHNfc29ydGVkICU+JSBmaWx0ZXIoU21hbGxlciAlIWluJSByYW5raW5nczEkQmlnZ2VyKSAlPiUgIGdyb3VwX2J5KFNtYWxsZXIpICU+JSBzdW1tYXJpc2UoQ291bnQ9bigpKSU+JSAgYXJyYW5nZShDb3VudCkKICAgIG5hbWVzKHJhbmtpbmdzMilbMV0gPC0gIkJpZ2dlciIKICAgIHJhbmtpbmdzIDwtIHJiaW5kKHJhbmtpbmdzMSxyYW5raW5nczIpCgogICAgcmFua2luZ3MkUmFuayA8LSAgc2VxLmludChucm93KHJhbmtpbmdzKSkgCiAgICByYW5raW5ncyAlPD4lIGRwbHlyOjpzZWxlY3QoLUNvdW50KQogICAgcmVzdWx0c19zb3J0ZWQgPC0gbGVmdF9qb2luKHJlc3VsdHNfc29ydGVkLCByYW5raW5ncykgJT4lICBhcnJhbmdlKFJhbmspCgogCiAgICByYW5raW5ncyRUdWtleVJhbmsgPC0gTkEKICAgIHJhbmtpbmdzJFR1a2V5UmFua1sxXSA8LSAxCiAgICBmb3IoIGkgaW4gMjpucm93KHJhbmtpbmdzKSl7CiAgICAgIGlmKHJlc3VsdHNfc29ydGVkJGFkai5wLnZhbHVlW3Jlc3VsdHNfc29ydGVkJEJpZ2dlcj09cmFua2luZ3MkQmlnZ2VyW2ktMV0mcmVzdWx0c19zb3J0ZWQkU21hbGxlciA9PSByYW5raW5ncyRCaWdnZXJbaV1dPD0wLjA1KXsKICAgICAgICByYW5raW5ncyRUdWtleVJhbmtbaV0gPC0gcmFua2luZ3MkVHVrZXlSYW5rW2ktMV0rMQogICAgICB9IGVsc2UgaWYoc3VtKChyZXN1bHRzX3NvcnRlZCRTbWFsbGVyW3Jlc3VsdHNfc29ydGVkJEJpZ2dlcj09cmFua2luZ3MkQmlnZ2VyW2ktMV0mcmVzdWx0c19zb3J0ZWQkYWRqLnAudmFsdWU8PTAuMDVdICVpbiUgcmVzdWx0c19zb3J0ZWQkU21hbGxlcltyZXN1bHRzX3NvcnRlZCRCaWdnZXI9PXJhbmtpbmdzJEJpZ2dlcltpXSZyZXN1bHRzX3NvcnRlZCRhZGoucC52YWx1ZT4wLjA1XSk+MCkpewogICAgICAgIHJhbmtpbmdzJFR1a2V5UmFua1tpXSA8LSByYW5raW5ncyRUdWtleVJhbmtbaS0xXSsxCiAgICAgICB9IGVsc2UgewogICAgICAgICByYW5raW5ncyRUdWtleVJhbmtbaV0gPC0gcmFua2luZ3MkVHVrZXlSYW5rW2ktMV0KICAgICAgIH0KICAgICB9CiAgICAKICAgIFR1a2V5X3JhbmtlZCA8LSAgcmFua2luZ3MgJT4lICBkcGx5cjo6c2VsZWN0KEJpZ2dlcixUdWtleVJhbmspCiAgICBuYW1lcyhUdWtleV9yYW5rZWQpWzFdIDwtICAiUGxhbm5pbmcuQXJlYSIKICAgIFBsYW5uaW5nX01lYW4gPC0gIGRhdGFfcHJvcGVydHkgJT4lIGdyb3VwX2J5KFBsYW5uaW5nLkFyZWEpICU+JSAgc3VtbWFyaXNlKG1lYW49IG1lYW4oVW5pdC5QcmljZS4uLi5wc20uKSwgc2Q9IHNkKFVuaXQuUHJpY2UuLi4ucHNtLikpCiAgICBUdWtleV9yYW5rZWQgPC0gbGVmdF9qb2luKFR1a2V5X3JhbmtlZCwgUGxhbm5pbmdfTWVhbikKICAgIAogICAgYXNzaWduKHBhc3RlKCJhb3ZfIiwgUHJvcFR5cGVba10sIHNlcCA9ICIiKSwgdGlkeShyZXMuYW92KSkKICAgIGFzc2lnbihwYXN0ZSgiVHVrZXlfIiwgUHJvcFR5cGVba10sIHNlcCA9ICIiKSwgYXMuZGF0YS5mcmFtZShUdWtleV9yYW5rZWQpKQp9CgpoZWFkKFR1a2V5X0FwYXJ0bWVudCkKaGVhZChUdWtleV9Db25kb21pbml1bSkKaGVhZChgVHVrZXlfRXhlY3V0aXZlIENvbmRvbWluaXVtYCkKaGVhZChgVHVrZXlfRGV0YWNoZWQgSG91c2VgKQpoZWFkKGBUdWtleV9TZW1pLURldGFjaGVkIEhvdXNlYCkKaGVhZChgVHVrZXlfVGVycmFjZSBIb3VzZWApCgpzdW1tYXJ5KHJlcy5hb3YpCmBgYApgYGB7cn0KI0RldGFpbGVkIGFuYWx5c2lzIAoKI1dlIHBlcmZvcm1lZCB0aGUgQU5PVkEgdGVzdCB0byBkZXRlcm1pbmUgaWYgYWxsIHRoZSBtZWFuIFBTTSBwZXIgcGxhbm5pbmcgYXJlYXMgd2VyZSBzaW1pbGFyLgojQXQgOTUlIENvbmZpZGVuY2UgbGV2ZWwsIGFzIFAgdmFsdWUgaXMgbGVzcyB0aGFuIDAuMDUsIHdlIGhhdmUgc3VmZmljaWVudCBldmlkZW5jZSB0byBzYXkgdG8gcmVqZWN0IEgwIGFuZCB0aGF0IHRoZSBtZWFuIHByaWNlIHBlciBzcXVhcmUgbWV0ZXIocHNtKSBvZiBmbGF0cyBhcmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQKI1RvIGNvbXBhcmUgdGhlIGdyb3VwIG1lYW5zLCBhIHBvc3QgaG9jIHRlc3QgLSAgVFVLRVkgdGVzdCAtIHdhcyBwZXJmb3JtZWQgcGVyIHByb3BlcnR5IHR5cGUgCiNHaXZlbiB0aGUgZXh0ZW5zaXZlIHJlc3VsdHMgb3V0cHV0LCB0aGUgVFVLRVkgcmVzdWx0cyB3ZXJlIHNvcnRlZCBmb3IgY2xhcml0eS4gCiNGb3IgZWFjaCBwcm9wZXJ0eSB0eXBlLCB0aGUgZGlmZmVyZW5jZXMgd2VyZSBzb3J0ZWQsIGZpbHRlcmVkIGZvciByZXN1bHRzIHdpdGggYWRqdXN0ZWQgcC12YWx1ZSA8PTAuMDUgYW5kIHRoZW4gcmFua2VkIGFjY29yZGluZ2x5IAojQXQgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzOgojICAgRm9yIHByb3BlcnR5IHR5cGUgLSBhcGFydG1lbnRzIC0gdGhlIE9yY2hhcmQgYXJlYSBjb21wYXJlZCB3aXRoIHRoZSBvdGhlciBwbGFubmluZyBhcmVhcywgaGFzIHRoZSBtb3N0IHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGRpZmZlcmVuY2VzIGJldHdlZW4gbWVhbnMuIFJpdmVyIFZhbGxleSwgTmV3dG9uIGFuZCBEb3dudG93biBjb3JlIGFyZSByYW5rZWQgMm5kLgojICAgRm9yIHByb3BlcnR5IHR5cGUgLSBDb25kb21pbml1bSAtIHRoZSBPcmNoYXJkIGFuZCBSaXZlciBWYWxseWUgYXJlYXMgY29tcGFyZWQgd2l0aCB0aGUgb3RoZXIgcGxhbm5pbmcgYXJlYXMsIGhhdmUgdGhlIG1vc3Qgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZGlmZmVyZW5jZXMgYmV0d2VlbiBtZWFucy4gVGhlIE5ld3RvbiBhcmVhIGNvbWVzIGluIHNlY29uZAojICAgRm9yIHByb3BlcnR5IHR5cGUgLSBFQyAtIHRoZSBCaXNoYW4gYXJlYSBjb21wYXJlZCB3aXRoIHRoZSBvdGhlciBwbGFubmluZyBhcmVhcywgaGFzIHRoZSBtb3N0IHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGRpZmZlcmVuY2VzIGJldHdlZW4gbWVhbnMuU2VuZ2thbmcsIFB1bmdnb2wgYXJlYXMgY29tZSBpbiBzZWNvbmQgCiMgICBGb3IgcHJvcGVydHkgdHlwZSAtIERldGFjaGVkIEhvdXNlIC0gdGhlIE5ld3RvbiBhcmVhIGNvbXBhcmVkIHdpdGggdGhlIG90aGVyIHBsYW5uaW5nIGFyZWFzLCBoYXMgdGhlIG1vc3Qgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZGlmZmVyZW5jZXMgYmV0d2VlbiBtZWFucy4gVGFuZ2xpbiBhbmQgTm92ZW5hIC8gU291dGhlcm4gaXNsYW5kcyAoU2VudG9zYSkgYXJlIHJhbmtlZCBzZWNvbmQgYW5kIHRoaXJkIHJlc3BlY3RpdmVseQojICAgRm9yIHByb3BlcnR5IHR5cGUgLSBTZW1pLURldGFjaGVkIEhvdXNlIC0gdGhlIFJpdmVyIFZhbGxleSBhcmVhIGNvbXBhcmVkIHdpdGggdGhlIG90aGVyIHBsYW5uaW5nIGFyZWFzLCBoYXMgdGhlIG1vc3Qgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZGlmZmVyZW5jZXMgYmV0d2VlbiBtZWFucy4gVGhlIFRhbmdsaW4sIE1hcmluZSBQYXJhZGUgYXJlYXMgYXJlIHJhbmtlZCBzZWNvbmQKIyAgIEZvciBwcm9wZXJ0eSB0eXBlIC0gVGVycmFuY2UgSG91c2UgLSB0aGUgTmV3dG9uIGFyZWEgY29tcGFyZWQgd2l0aCB0aGUgb3RoZXIgcGxhbm5pbmcgYXJlYXMsIGhhcyB0aGUgbW9zdCBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBkaWZmZXJlbmNlcyBiZXR3ZWVuIG1lYW5zLiBSb2Nob3IsIFJpdmVyIFZhbGxleSBhbmQgTm92ZWEgYXJlIGpvaW50IHNlY29uZC4gCgoKCgpgYGAKCiMjIyBBTk9WQSBhbmQgVHVrZXkgb24gVHJhbnNhY3RlZCBQcmljZSBwZXIgUGxhbm5pbmcgQXJlYSBmb3IgZWFjaCBGbGF0IFR5cGUKYGBge3J9CiMgb3V0cHV0IHRoZSBkYW1uIHR1a2V5IHRhYmxlIGFuZCBzb3J0CiMgamF5bmUgZG8gYW5vdmEKCgoKZm9yIChrIGluIDE6bGVuZ3RoKHVuaXF1ZShkYXRhJFByb3BlcnR5LlR5cGUpKSl7CiAgZGF0YV9wcm9wZXJ0eSA8LSAgZGF0YSAlPiUgIGZpbHRlcihQcm9wZXJ0eS5UeXBlPT11bmlxdWUoZGF0YSRQcm9wZXJ0eS5UeXBlKVtrXSkKICAgIHJlcy5hb3YgPC0gYW92KFRyYW5zYWN0ZWQuUHJpY2UuLi4uflBsYW5uaW5nLkFyZWEsZGF0YT1kYXRhX3Byb3BlcnR5KQogICAgc3VtbWFyeShyZXMuYW92KQogICAgcmVzdWx0cyA8LSAgdGlkeShUdWtleUhTRChyZXMuYW92LG9yZGVyZWQ9VFJVRSkpCiAgICByZXN1bHRzX3NvcnRlZCA8LSAgcmVzdWx0cyAlPiUgc2VwYXJhdGUoY29tcGFyaXNvbiwgYygiQmlnZ2VyIiwgIlNtYWxsZXIiKSxzZXAgPSAiLSIpCiAgICAKICAKICAgIHJhbmtpbmdzMSA8LSAgcmVzdWx0c19zb3J0ZWQgJT4lICBncm91cF9ieShCaWdnZXIpICU+JSAgc3VtbWFyaXNlKENvdW50PW4oKSkgJT4lICBhcnJhbmdlKGRlc2MoQ291bnQpKQogICAgcmFua2luZ3MyIDwtIHJlc3VsdHNfc29ydGVkICU+JSBmaWx0ZXIoU21hbGxlciAlIWluJSByYW5raW5nczEkQmlnZ2VyKSAlPiUgIGdyb3VwX2J5KFNtYWxsZXIpICU+JSBzdW1tYXJpc2UoQ291bnQ9bigpKSU+JSAgYXJyYW5nZShDb3VudCkKICAgIG5hbWVzKHJhbmtpbmdzMilbMV0gPC0gIkJpZ2dlciIKICAgIHJhbmtpbmdzIDwtIHJiaW5kKHJhbmtpbmdzMSxyYW5raW5nczIpCgogICAgcmFua2luZ3MkUmFuayA8LSAgc2VxLmludChucm93KHJhbmtpbmdzKSkgCiAgICByYW5raW5ncyAlPD4lIGRwbHlyOjpzZWxlY3QoLUNvdW50KQogICAgcmVzdWx0c19zb3J0ZWQgPC0gbGVmdF9qb2luKHJlc3VsdHNfc29ydGVkLCByYW5raW5ncykgJT4lICBhcnJhbmdlKFJhbmspCgogCiAgICByYW5raW5ncyRUdWtleVJhbmsgPC0gTkEKICAgIHJhbmtpbmdzJFR1a2V5UmFua1sxXSA8LSAxCiAgICBmb3IoIGkgaW4gMjpucm93KHJhbmtpbmdzKSl7CiAgICAgIGlmKHJlc3VsdHNfc29ydGVkJGFkai5wLnZhbHVlW3Jlc3VsdHNfc29ydGVkJEJpZ2dlcj09cmFua2luZ3MkQmlnZ2VyW2ktMV0mcmVzdWx0c19zb3J0ZWQkU21hbGxlciA9PSByYW5raW5ncyRCaWdnZXJbaV1dPD0wLjA1KXsKICAgICAgICByYW5raW5ncyRUdWtleVJhbmtbaV0gPC0gcmFua2luZ3MkVHVrZXlSYW5rW2ktMV0rMQogICAgICB9IGVsc2UgaWYoc3VtKChyZXN1bHRzX3NvcnRlZCRTbWFsbGVyW3Jlc3VsdHNfc29ydGVkJEJpZ2dlcj09cmFua2luZ3MkQmlnZ2VyW2ktMV0mcmVzdWx0c19zb3J0ZWQkYWRqLnAudmFsdWU8PTAuMDVdICVpbiUgcmVzdWx0c19zb3J0ZWQkU21hbGxlcltyZXN1bHRzX3NvcnRlZCRCaWdnZXI9PXJhbmtpbmdzJEJpZ2dlcltpXSZyZXN1bHRzX3NvcnRlZCRhZGoucC52YWx1ZT4wLjA1XSk+MCkpewogICAgICAgIHJhbmtpbmdzJFR1a2V5UmFua1tpXSA8LSByYW5raW5ncyRUdWtleVJhbmtbaS0xXSsxCiAgICAgICB9IGVsc2UgewogICAgICAgICByYW5raW5ncyRUdWtleVJhbmtbaV0gPC0gcmFua2luZ3MkVHVrZXlSYW5rW2ktMV0KICAgICAgIH0KICAgICB9CiAgICAKICAgIFR1a2V5X3JhbmtlZCA8LSAgcmFua2luZ3MgJT4lICBkcGx5cjo6c2VsZWN0KEJpZ2dlcixUdWtleVJhbmspCiAgICBuYW1lcyhUdWtleV9yYW5rZWQpWzFdIDwtICAiUGxhbm5pbmcuQXJlYSIKICAgIFBsYW5uaW5nX01lYW4gPC0gIGRhdGFfcHJvcGVydHkgJT4lIGdyb3VwX2J5KFBsYW5uaW5nLkFyZWEpICU+JSAgc3VtbWFyaXNlKG1lYW49IG1lYW4oVHJhbnNhY3RlZC5QcmljZS4uLi4pLCBzZD0gc2QoVHJhbnNhY3RlZC5QcmljZS4uLi4pKQogICAgVHVrZXlfcmFua2VkIDwtIGxlZnRfam9pbihUdWtleV9yYW5rZWQsIFBsYW5uaW5nX01lYW4pCiAgICAKICAgIGFzc2lnbihwYXN0ZSgiYW92XyIsIFByb3BUeXBlW2tdLCBzZXAgPSAiIiksIHRpZHkocmVzLmFvdikpCiAgICBhc3NpZ24ocGFzdGUoIlR1a2V5XyIsIFByb3BUeXBlW2tdLCBzZXAgPSAiIiksIGFzLmRhdGEuZnJhbWUoVHVrZXlfcmFua2VkKSkKfQoKaGVhZChUdWtleV9BcGFydG1lbnQpCmhlYWQoVHVrZXlfQ29uZG9taW5pdW0pCmhlYWQoYFR1a2V5X0V4ZWN1dGl2ZSBDb25kb21pbml1bWApCmhlYWQoYFR1a2V5X0RldGFjaGVkIEhvdXNlYCkKaGVhZChgVHVrZXlfU2VtaS1EZXRhY2hlZCBIb3VzZWApCmhlYWQoYFR1a2V5X1RlcnJhY2UgSG91c2VgKQpzdW1tYXJ5KHJlcy5hb3YpCgpgYGAKYGBge3J9CiNEZXRhaWxlZCBhbmFseXNpcyAKCiNXZSBwZXJmb3JtZWQgdGhlIEFOT1ZBIHRlc3QgdG8gZGV0ZXJtaW5lIGlmIHRoZSBtZWFuIHRyYW5zYWN0ZWQgcHJpY2UgcGVyIHBsYW5uaW5nIGFyZWFzIHdlcmUgc2ltaWxhci4KI0F0IDk1JSBDb25maWRlbmNlIGxldmVsLCBhcyBQIHZhbHVlIGlzIGxlc3MgdGhhbiAwLjA1LCB3ZSBoYXZlIHN1ZmZpY2llbnQgZXZpZGVuY2UgdG8gc2F5IHRvIHJlamVjdCBIMCBhbmQgdGhhdCB0aGUgbWVhbiB0cmFuc2FjdGVkIHByaWNlIG9mIGZsYXRzIGFyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudAojVG8gY29tcGFyZSB0aGUgZ3JvdXAgbWVhbnMsIGEgcG9zdCBob2MgdGVzdCAtICBUVUtFWSB0ZXN0IC0gd2FzIHBlcmZvcm1lZCBmb3IgZWFjaCBwcm9wZXJ0eSB0eXBlIAojR2l2ZW4gdGhlIGV4dGVuc2l2ZSByZXN1bHRzIG91dHB1dCwgdGhlIFRVS0VZIHJlc3VsdHMgd2VyZSBzb3J0ZWQgZm9yIGNsYXJpdHkuIAojRm9yIGVhY2ggcHJvcGVydHkgdHlwZSwgdGhlIGRpZmZlcmVuY2VzIHdlcmUgc29ydGVkLCBmaWx0ZXJlZCBmb3IgcmVzdWx0cyB3aXRoIGFkanVzdGVkIHAtdmFsdWUgPD0wLjA1IGFuZCB0aGVuIHJhbmtlZCBhY2NvcmRpbmdseSAKI0F0IDk1JSBjb25maWRlbmNlIGludGVydmFsczoKIyAgIEZvciBwcm9wZXJ0eSB0eXBlIC0gYXBhcnRtZW50cyAtIHRoZSBPcmNoYXJkIGFyZWEgY29tcGFyZWQgd2l0aCB0aGUgb3RoZXIgcGxhbm5pbmcgYXJlYXMsIGhhcyB0aGUgbW9zdCBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBkaWZmZXJlbmNlcyBiZXR3ZWVuIG1lYW5zLiBOZXd0b24gYW5kIERvd250b3duIGNvcmUgYXJlIHJhbmtlZCAybmQuVGhpcyBpcyAKIyAgIEZvciBwcm9wZXJ0eSB0eXBlIC0gQ29uZG9taW5pdW0gLSB0aGUgTmV3dG9uIGFuZCBUYW5nbGluIGFyZWFzIGNvbXBhcmVkIHdpdGggdGhlIG90aGVyIHBsYW5uaW5nIGFyZWFzLCBoYXZlIHRoZSBtb3N0IHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGRpZmZlcmVuY2VzIGJldHdlZW4gbWVhbnMuIE9yY2hhcmQgYW5kIFJpdmVyIFZhbGxleSBhcmVhcyBjb21lIGluIHNlY29uZAojICAgRm9yIHByb3BlcnR5IHR5cGUgLSBFQyAtIHRoZSBCaXNoYW4gYXJlYSBjb21wYXJlZCB3aXRoIHRoZSBvdGhlciBwbGFubmluZyBhcmVhcywgaGFzIHRoZSBtb3N0IHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGRpZmZlcmVuY2VzIGJldHdlZW4gbWVhbnMuIEFuZyBNbyBLaW8gYW5kIEJ1a2l0IEJhdG9rIGFyZWFzIGFyZSByYW5rZWQgc2Vjb25kIGFuZCB0aGlyZCByZXNwZWN0aXZlbHkgCiMgICBGb3IgcHJvcGVydHkgdHlwZSAtIERldGFjaGVkIEhvdXNlIC0gdGhlIE5ld3RvbiBhcmVhIGNvbXBhcmVkIHdpdGggdGhlIG90aGVyIHBsYW5uaW5nIGFyZWFzLCBoYXMgdGhlIG1vc3Qgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZGlmZmVyZW5jZXMgYmV0d2VlbiBtZWFucy4gVGFuZ2xpbiBhbmQgU291dGhlcm4gaXNsYW5kcyAoU2VudG9zYSkgLyBCdWtpdCBUaW1haCAvIE5vdmVuYSAvIE1hcmluZSBQYXJhZGUgYXJlIHJhbmtlZCBzZWNvbmQgYW5kIHRoaXJkIHJlc3BlY3RpdmVseQojICAgRm9yIHByb3BlcnR5IHR5cGUgLSBTZW1pLURldGFjaGVkIEhvdXNlIC0gdGhlIFRhbmdsaW4gYXJlYSBjb21wYXJlZCB3aXRoIHRoZSBvdGhlciBwbGFubmluZyBhcmVhcywgaGFzIHRoZSBtb3N0IHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGRpZmZlcmVuY2VzIGJldHdlZW4gbWVhbnMuIFRoZSBSaXZlciBWYWxsZXkgYXJlYSBhcmUgcmFua2VkIHNlY29uZAojICAgRm9yIHByb3BlcnR5IHR5cGUgLSBUZXJyYW5jZSBIb3VzZSAtIHRoZSBOZXd0b24gYXJlYSBjb21wYXJlZCB3aXRoIHRoZSBvdGhlciBwbGFubmluZyBhcmVhcywgaGFzIHRoZSBtb3N0IHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGRpZmZlcmVuY2VzIGJldHdlZW4gbWVhbnMuIFRhbmdsaW4gYW5kIE5vdmVuYSBhcmVhcyBhcmUgam9pbnQgc2Vjb25kLiAKCgoKYGBgCgojIyMgTVJUIFN0YXRpb25zKEV4aXN0aW5nIGFuZCBQbGFubmVkKSBieSBQbGFubmluZyBBcmVhCmBgYHtyfQptcnQgPC0gcmVhZC5jc3YoImRhdGEvUGxhbm5pbmdfYXJlYV9tcnRfc3RhdGlvbnMuY3N2IikKbXJ0JFBsYW5uaW5nLkFyZWEgPC0gdG91cHBlcihtcnQkw68uLlBsYW5uaW5nLkFyZWEpCmRncCA8LSAgY2VudHJvaWRzIDwtIHJlYWRPR1IoImRhdGEvTVAxNF9QTE5HX0FSRUFfV0VCX1BMLnNocCIpCmRncCA8LSAgc3BUcmFuc2Zvcm0oZGdwLCBDUlMoIitwcm9qPWxvbmdsYXQgK2VsbHBzPUdSUzgwIikpCm1ydDIgPC0gbWVyZ2UoZGdwLG1ydCwgYnkueCA9J1BMTl9BUkVBX04nLCBieS55ID0gJ1BsYW5uaW5nLkFyZWEnKQoKI0FsbCBNUlQgU3RhdGlvbnMKbXJ0X3BhbCA8LSBjb2xvckZhY3RvcihwYWxldHRlPSBicmV3ZXIucGFsKDE1LCAnUmRZbEduJyksCiAgICAgICAgICAgICAgICAgICAgICAgIGRvbWFpbj1jKDAsMSwyLDMsNCw1LDYsNyw4LDksMTAsMTEsMTIsMTMsMTQpLAogICAgICAgICAgICAgICAgICAgICAgICBuYS5jb2xvciA9ICIjMDAwMDAwMDAiKQoKIApsZWFmbGV0KGRncCkgJT4lIGFkZFRpbGVzKCkgJT4lIGFkZFBvbHlnb25zKGZpbGxDb2xvciA9IH5tcnRfcGFsKG1ydDIkVG90YWwubm8uLm9mLnN0YXRpb25zKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHQgPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImdyZXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhc2hBcnJheSA9ICIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuOCkgJT4lIGFkZExlZ2VuZCgidG9wcmlnaHQiLCBtcnRfcGFsLCB2YWx1ZXM9KDA6MTQpLCB0aXRsZT0gIk1SVCBTdGF0aW9ucyIsIGxhYkZvcm1hdCA9IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxGb3JtYXQoc3VmZml4ID0gIiBzdGF0aW9ucyIpKQoKI09ubHkgY3VycmVudCBNUlQgc3RhdGlvbnMKbXJ0X3BhbDIgPC0gY29sb3JGYWN0b3IocGFsZXR0ZT0gYnJld2VyLnBhbCgxMiwgJ1JkWWxHbicpLAogICAgICAgICAgICAgICAgICAgICAgICBkb21haW49YygwLDEsMiwzLDQsNSw2LDcsOCw5LDEwLDExKSwKICAgICAgICAgICAgICAgICAgICAgICAgbmEuY29sb3IgPSAiIzAwMDAwMDAwIikKbGVhZmxldChkZ3ApICU+JSBhZGRUaWxlcygpICU+JSBhZGRQb2x5Z29ucyhmaWxsQ29sb3IgPSB+bXJ0X3BhbDIobXJ0MiROby4ub2Yub3BlcmF0aW9uYWwuc3RhdGlvbnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdlaWdodCA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZ3JleSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGFzaEFycmF5ID0gIjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMC44KSAlPiUgYWRkTGVnZW5kKCJ0b3ByaWdodCIsIG1ydF9wYWwyLCB2YWx1ZXM9KDA6MTEpLCB0aXRsZT0gIk1SVCBTdGF0aW9ucyIsIGxhYkZvcm1hdCA9IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxGb3JtYXQoc3VmZml4ID0gIiBzdGF0aW9ucyIpKQoKYGBgCgojIyBEZXRhaWxlZCBBbmFseXNpcwoK